/*
sigio 是一个与异步 I/O 通知相关的 Unix/Linux 编程概念。
它通常指的是使用 SIGIO 信号（或在某些系统上称为 SIGPOLL）来通知进程某个文件描述符已经准备好进行 I/O 操作。
这是一种传统的异步 I/O 通知机制，但现在更常用 select, poll, epoll (Linux) 或 kqueue (BSD) 等更现代的方法。

在信号处理函数中进行 I/O： 在 io_handler 函数中，您可以对触发信号的文件描述符进行读或写操作。但需要注意：
    信号处理函数应尽可能简单，避免调用非异步信号安全的函数（如 printf, malloc）。
    对于 UDP 套接字，这种方式很有效，因为每个数据报都是独立的。信号到来通常意味着有一个完整的数据报可读。
    对于 TCP 套接字，情况复杂得多。因为TCP是流式协议，收到SIGIO并不保证能读取多少数据，也可能是因为连接关闭或错误。因此SIGIO很少用于TCP
*/

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

#include <stdio.h>
#include <string.h>

// int fcntl(int fd, int cmd, ... /* arg */);

int fd = -1;

void handle(int no)
{
    if (no == SIGIO)
    {
        printf("got SIGIO\n");

        /*
        局限性：
        收到了信号，但是不知道是可读还是可写
        */
    }
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("usage:%s <dev>\n", argv[0]);
        return -1;
    }

    int ret = -1;

    fd = open(argv[1], O_CLOEXEC | O_RDWR);
    if (fd < 0)
    {
        printf("open %s error(%s)\n", argv[1], strerror(errno));
        return -1;
    }

    // 设置文件描述符的属主
    ret = fcntl(fd, F_SETOWN, getpid());
    if (ret == -1)
    {
        printf("fcntl F_SETOWN error(%s)\n", strerror(errno));
        close(fd);
        return -1;
    }

    /*
    F_GETFD 文件描述符层（内核级）
    F_GETFL 文件打开实例层（文件表项）
    */
    int flags = fcntl(fd, F_GETFL);
    ret = fcntl(fd, F_SETFL, flags | O_ASYNC); // 添加异步标识
    if (ret == -1)
    {
        printf("fcntl F_SETFL error(%s)\n", strerror(errno));
        close(fd);
        return -1;
    }

    (void)signal(SIGIO, handle);

    while (1)
    {
        sleep(3);
    }

    close(fd);

    return 0;
}